import { Injectable, Optional, Inject } from '@angular/core';

import { isNumber } from '../../util';
import { BroadcastService } from './broadcast.service';
import { STORE_PREFIX, STORE_VERSION, DISABLED_INIT_LOAD_STORE_KEY } from '../keys';
import { defaultTable, IndexDbService } from './index-db.service';


@Injectable({
  providedIn: 'root'
})
export class StoreService<B, S> {

  private localStore = {};
  private sessionStore = {};

  private disabledInitLoadKey: string[];

  constructor(
    @Optional() @Inject(STORE_PREFIX) private storePrefix,
    @Optional() @Inject(STORE_VERSION) private storeVersion,
    @Inject(DISABLED_INIT_LOAD_STORE_KEY) disabledInitLoadKey: string[][],
    private broadcastService: BroadcastService<B>,
    private indexDbService: IndexDbService,
  ) {
    this.storePrefix = this.storePrefix ? this.storePrefix : 'store-';
    this.storeVersion = this.storeVersion ? this.storeVersion : 1;
    this.disabledInitLoadKey = [...disabledInitLoadKey.reduce((set, currKeys) => {
      currKeys.forEach((key) => set.add(key));
      return set;
    }, new Set<string>())];

    window.addEventListener('storage', (e: any) => {
      const data = {
        key: e.key ? e.key.replace(this.storePrefix, '') : '',
        newValue: JSON.parse(e.newValue || '{}'),
        oldValue: JSON.parse(e.oldValue || '{}')
      };
      this.broadcastService.emit(<any>'StoreChange', data);
    });
  }

  public loadData() {
    this.loadDataFromStorage(window.localStorage, this.localStore);
    this.loadDataFromStorage(window.sessionStorage, this.sessionStore);
  }

  public getItem(key: S, useSession = false): any {
    const store = useSession ? this.sessionStore : this.localStore;
    const storage = useSession ? window.sessionStorage : window.localStorage;

    // 内存中不存在时，自动读取离线缓存
    if ((<any>store)[key] === undefined) {// 避免重复读取不存在的值
      try {
        const data = JSON.parse(storage.getItem(this.storePrefix + key));
        (<any>store)[key] = data;
      } catch (e) { } finally {// 避免重复读取不存在的值
        if ((<any>store)[key] === undefined) {
          (<any>store)[key] = null;
        }
      }
    }

    return (<any>store)[key];
  }

  public setItem(key: S, value: any, useSession = false): any {
    const store = useSession ? this.sessionStore : this.localStore;
    const storage = useSession ? window.sessionStorage : window.localStorage;

    if (value !== null || value !== undefined) {
      (<any>store)[key] = value;
      storage.setItem(this.storePrefix + key, JSON.stringify(value));
    } else {
      (<any>store)[key] = null;
      storage.removeItem(this.storePrefix + key);
    }
  }

  public clearItems() {
    this.clearStorage(window.sessionStorage);
    this.clearStorage(window.localStorage);
    this.localStore = null;
    this.sessionStore = null;
  }

  /**
   * 添加数据
   * @param key string
   * @param store_name string
   * @param data any 值为 undefined 时执行删除逻辑
   */
  public setData(key: string, store_name: defaultTable, data?: any) {
    return this.indexDbService.setData(key, store_name, data);
  }

  /**
   * 查询数据
   * @param key string
   * @param store_name string
   */
  public getData(key: string, store_name: defaultTable) {
    return this.indexDbService.getData(key, store_name);
  }

  /**
   * 清空对象仓库
   * @param store_name 值为 undefined 时，清空所有对象仓库
   */
  public clearStore(store_name?: defaultTable) {
    this.indexDbService.clearStore(store_name);
  }

  public getItemByUID(key: S, useSession = false): any {
    const [UID, ...keys] = this.getItem(<any>'UID', useSession) || [null];
    if (UID && keys.includes(key)) {
      return this.getItem(<any>key, useSession);
    }
  }

  public setItemByUID(key: S, value: any, useSession = false) {
    const [UID, ...keys] = this.getItem(<any>'UID', useSession) || [null];
    if (UID) {
      this.setItem(<any>key, value, useSession);
      if (!keys.includes(key)) {
        this.setItem(<any>'UID', [UID, ...keys, key], useSession);
      }
    }
  }

  public resetUID(uid: string) {
    let [UID, ...keys] = this.getItem(<any>'UID', false) || [null];
    if (!UID) {
      this.setItem(<any>'UID', [uid], false);
    } else if (UID !== uid) {
      this.clearStorageByUID(keys, false);
      this.setItem(<any>'UID', [uid], false);
    }

    [UID, ...keys] = this.getItem(<any>'UID', true) || [null];
    if (!UID) {
      this.setItem(<any>'UID', [uid], true);
    } else if (UID !== uid) {
      this.clearStorageByUID(keys, true);
      this.setItem(<any>'UID', [uid], true);
    }
  }

  private clearStorageByUID(keys: string[], useSession = false) {
    const store = useSession ? this.sessionStore : this.localStore;
    const storage = useSession ? window.sessionStorage : window.localStorage;

    for (let i = 0; i < storage.length; i++) {
      const key: string = storage.key(i).replace(this.storePrefix, '');
      if (keys.includes(key)) {
        (<any>store)[key] = null;
        try {
          storage.removeItem(this.storePrefix + key);
          i--;
        } catch (e) { }
      }
    }
  }

  // 从本地加载数据
  private loadDataFromStorage(storage: Storage, store) {
    // 根据版本号判断是否需要清除离线数据
    const version = storage.getItem(this.storePrefix + 'version');
    if (!isNumber(+version) || this.storeVersion > +version) {
      storage.clear();
      storage.setItem(this.storePrefix + 'version', this.storeVersion);
      return;
    }

    for (let i = 0; i < storage.length; i++) {
      const key: string = storage.key(i);
      const prop = key.split('-')[1];
      if (key.startsWith(this.storePrefix) && prop && !this.disabledInitLoadKey.includes(prop)) {
        try {
          store[prop] = JSON.parse(storage.getItem(key));
        } catch (e) {
          store[prop] = null;
        }
      }
    }
  }

  private clearStorage(storage: Storage) {
    for (let i = 0; i < storage.length; i++) {
      const key: string = storage.key(i);
      const prop = key.split('-')[1];
      if (key.startsWith(this.storePrefix) && prop) {
        try {
          storage.removeItem(key);
          i--;
        } catch (e) { }
      }
    }
  }

}
